A quick intro to the intro to R Lesson Series
This ‘Intro to R Lesson Series’ is brought to you by the Centre for the Analysis of Genome Evolution & Function’s (CAGEF) bioinformatics training initiative. This course was developed based on feedback on the needs and interests of the Department of Cell & Systems Biology and the Department of Ecology and Evolutionary Biology.
This lesson is the last in a 6-part series. Hopefully you have learned a bit about how to import and manipulate data, make exploratory plots, perform some basic statistical tests, test a regression model, and make some even prettier plots and documents to share to results.
Today we are going to learn to write functions, which can save you time and help scale up your analyses. It will also help to minimize copy and pasting, redundancy, and feeling like a newb.
The structure of the class is a code-along style. It is hands on. The lecture AND code we are going through are available on GitHub for download at https://github.com/eacton/CAGEF, so you can spend the time coding and not taking notes. As we go along, there will be some challenge questions and multiple choice questions on Socrative. At the end of the class if you could please fill out a post-lesson survey (https://www.surveymonkey.com/r/X5C2JGR), it will help me further develop this course and would be greatly appreciated.
Packages Used in This Lesson
The following packages are used in this lesson:
tidyverse (ggplot2, tidyr, dplyr)
testthat assertthat
Please install and load these packages for the lesson. In this document I will load each package separately, but I will not be reminding you to install the package. Remember: these packages may be from CRAN OR Bioconductor.
Highlighting
grey background - a package, function, code or command
italics - an important term or concept
bold - heading or a term that is being defined
blue text - named or unnamed hyperlink
Objective: At the end of this session you will be able to write functions, making your coding more efficient, understandable, reproducible, and hopefully less frustrating.
Load the packages!
library(tidyverse)
library(testthat)
library(assertr)
Error in library(assertr) : there is no package called ‘assertr’
Dataset: Pyrosequencing of the V3-V5 hypervariable regions of the 16S rRNA gene
16S rRNA pyrosequencing of 30 latrines from Tanzania and Vietnam at different depths (multiples of 20cm). Microbial abundance is represented in Operational Taxonomic Units (OTUs). Operational Taxonomic Units (OTUs) are groups of organisms defined by a specified level of DNA sequence similarity at a marker gene (e.g. 97% similarity at the V4 hypervariable region of the 16S rRNA gene). Intrinsic environmental factors such as pH, temperature, organic matter composition were also recorded.
We have 2 csv files:
- A metadata file (Naming conventions: [Country_LatrineNo_Depth]) with sample names and environmental variables.
- OTU abundance table.
B Torondel, JHJ Ensink, O Gunvirusdu, UZ Ijaz, J Parkhill, F Abdelahi, V-A Nguyen, S Sudgen, W Gibson, AW Walker, and C Quince. Assessment of the influence of intrinsic environmental and geographical factors on the bacterial ecology of pit latrines Microbial Biotechnology, 9(2):209-223, 2016. DOI:10.1111/1751-7915.12334
Why Functions?
dat <- read.csv("data/long_SPE_pitlatrine.csv", header = T, stringsAsFactors = F)
I want to know the median and median absolute deviation of OTUs in all of our samples for Clostrida.
Great. What about the median and median absolute deviation for Bacilli? Hmmmm. I guess I’ll just copy and paste the above code because it will take 2 seconds.
Maybe I’ll check out Bacteroidia, too… by copy and pasting AGAIN.
Come to think of it, there are a lot of zeros in this data. Maybe I should be looking at mean and standard deviation instead of median and mad. I’ll just go back and change this in each of my 3 lines of code.
dat %>% filter(Taxa == "Clostridia") %>% summarize(mean = mean(OTUs), sd = sd(OTUs))
dat %>% filter(Taxa == "Bacilli") %>% summarize(mean = mean(OTUs), sd = sd(OTUs))
dat %>% filter(Taxa == "Bacteroidia") %>% summarize(mean = mean(OTUs), sd = sd(OTUs))
Does any of this feel familiar??
Functions allow us to:
- avoid repetition, make our code more compact
- avoid errors, copy/paste errors, typos
- versatile, updateable
- upscale, generalizable
- resuable, even for other projects!
The Structure of a Function
A function has 3 parts:
- formals are the input(s) of the function,
- the body is the braced expression,
- and the environment is the enclosure in which the function was defined.
We can look at these for a function we are familiar with, read.csv. Each argument is specified with $ and any default values are listed [1]. To use read.csv you must input a file - this argument has no default value. The rest of the arguments have a default value (such as header = TRUE), but can be modified by specifying the argument (by inputting header = FALSE). Most function defaults as ‘sensible’, but if you have not used the function before it is worth reading about the arguments in the help menu. ESPECIALLY do this with mathematical functions to see what assumptions you are making by using the default parameters. read.csv also has the magical ... argument. This argument allows you to pass arbitrary arguments to another function. We will see this in use shortly.
formals(read.csv)
$file
$header
[1] TRUE
$sep
[1] ","
$quote
[1] "\""
$dec
[1] "."
$fill
[1] TRUE
$comment.char
[1] ""
$...
The body of read.csv looks suspiciously like the function read.table because it is, indeed a ‘wrapper’ function which uses an existing function with different values for default arguments.
body(read.csv)
read.table(file = file, header = header, sep = sep, quote = quote,
dec = dec, fill = fill, comment.char = comment.char, ...)
The environment for read.csv is the utils package. When we make a function, it will be created in ‘.GlobalEnv’ the ‘global environment’. The global environment is our interactive workspace, viewed in the environment window in the top right, and can be thought of as a bag that holds all of our variables and functions. Note that we can use the dropdown menu to change the environment to package:utils, and we can find read.csv in that environment.
environment(read.csv)
<environment: namespace:utils>
Writing Functions
The basic form of a function takes in arguments (specifications by the user), has statements or expressions (what the function does), and returns an object. A function does not need to be named to be used, however, the greatest utility comes from reusing functions. A named function is called using parentheses: named_function(arguments).
Different from other programming languages, R is not picky about whitespace (the arrangement of tabs, spaces, indents). However formatting your function as below (statements indented below your arguments) will help to make it readable and familiar to other languages.
This may be a good time to point out that placing your cursor beside a closing bracket will highlight its corresponding opening bracket. This is really useful as your code gets more complex. Also, try deleting the closing bracket beside ‘object’. You will see that R is giving you a series of warnings (brackets highlighted in red, and ‘x’ beside 3 lines of code). Again, this is a helpful warning that saves you time from running an incomplete function.
named_function <-
function(arguments) {
statments
return(object)
}
We want to write a function that will calculate the mean and standard deviation for a given Taxa.
We can start by naming our function something vaguely informative. (Technically, you can name your functions and variables whatever you want. I could name them after Avengers characters, but people (or myself in 3 months) would have to work pretty hard to figure out what I was doing. Remember, with great power comes great responsibility).
Our function is going to take ‘Taxa’ as an argument. For some of you, when you type ‘{’ R will automatically give you your closing bracket ‘}’ so you won’t forget - ever trying to be helpful, thanks R. Next we can take our previous code, and save the output into an object called ‘summary_dat’. This is because I don’t want to overwrite my original data frame every time I run the function. We replace our specific Taxa, ‘Clostridia’, with our argument, ‘Taxa’. This is our expression. All we have to do now is return our data frame and make sure all of our brackets match.
Voila!
mean_func <-
function(Taxa) {
summary_dat <- filter(dat, Taxa == Taxa) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
return(summary_dat)
}
When you run this function, it will appear, named and with its arguments, in the global environment. If you click on the white box to the right of the function, you can see the code for your function. You can now use your function.
out <- mean_func("Bacteriodia")
[1] "This is the mean and sd for Bacteriodia"
mean_func <-
function(Taxa) {
filter(dat, Taxa == Taxa) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
print(paste('This is the mean for', Taxa))
}
Understanding Lexical Scoping
- why does dat get changed, but summary_dat does not end up being a global environmental variable when returned?
-may want to be able to take into different data frames…
mean_func <- function(dat, x) {
dat <- filter(dat, Taxa == x) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
}
mean_func(dat, "Clostridia")
Allows us to vary the Taxa and the functions.
mean_func("Clostridia", funs = list(mean = mean, median = median, sd = mean))
$mean
[1] 3423.407
$median
[1] 1432
$sd
[1] 3423.407
Defensive Programming
Testing Arguments and Validity of your Function
You might think that you are done with this function. You have the answers, right? But functions are not foolproof.
mean_func("Clostridia", funs = list(mean = quantile, median = median, sd = mean))
$mean
0% 25% 50% 75% 100%
0 496 1432 4761 17572
$median
[1] 1432
$sd
[1] 3423.407
Hmmm… mislabelled numbers could get a little confusing.
What if I wanted the summary statistics for 3 Taxa at once?
Uh oh. That’s bad. I didn’t want to merge the 3 Taxa… or did it take the last value?
Your next step is to try and intentionally break your function. Give it input that wasn’t meant for it and see what happens. Trouble-shooting this way is called ‘defensive programming’. What we are going to do is implement checks and balances such that our function can’t do anything weird.
Initial checks can be things like - what happens when I give an input that is numeric when I am expecting character data? What happens if there is an NA in the dataset? What happens if there is a typo? What happens if I am given an unexpected data structure? What happens if there is more or less data than I am expecting? What happens when I input logical data?
mean_func()
Error Handling
Unit Tests
-testthat assumption for function name = name?
Returns the last line… unless explicitly called with return
test_that("input is Taxa", {
expect_success(expect_type(mean_func("Bacilli"), type = "character"))
expect_failure(expect_type(mean_func(1), type = "character"))
})
[1] "This is the mean for Bacilli"
[1] "This is the mean for 1"
Error: Test failed: 'input is Taxa'
* Expectation did not fail
Assertions
In a perfect world, if something goes wrong while you are coding, you want an informative error message to reduce time trouble-shooting. We also want to do that for other people. Again, Hadley has come to the rescue with a way to test assertions and give meaningful error messages with the package assertthat.
Assertions are statements that are known to be TRUE, but we use whether these statements are TRUE or FALSE to our advantage in testing. For example, you can test whether the input to a function is a specific data type, length, or other custom argument by placing the assertion inside the function. You can also customize the output messages.
Let’s write some assertions into our function to test that:
-our input is a character vector -our input is in our list of Taxa (no typos!) -our input is only 1 value
We will then see how well this takes care of all of our inputs that ‘broke’ the function earlier. We will assess the error messages to see if they are clear.
function(Taxa) {
assert_that(is.character(Taxa)) %>%
validate_that(Taxa %in% dat$Taxa) %>%
assert_that(length(Taxa) == 1) %>%
summary <- filter(dat, Taxa == Taxa) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
return(summary)
print(paste('This is the mean for', Taxa,'. ', summary$mean))
}
function(Taxa) {
assert_that(is.character(Taxa)) %>%
validate_that(Taxa %in% dat$Taxa) %>%
assert_that(length(Taxa) == 1) %>%
summary <- filter(dat, Taxa == Taxa) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
return(summary)
print(paste('This is the mean for', Taxa,'. ', summary$mean))
}
I usually test to make sure it still works on something I expect it to when I make changes to my code.
mean_func(c("Clostridia", "Bacteriodia", "Bacilli"))
Error: assert_that: length of assertion is not 1
My function is still working on expected input.
For a vector of length 3, we receive the error that the ‘length of the assertion is not 1’. This could be more explicit for a user ie. “Please input only one Taxa”, but it is not bad. Note that using assert_that() we are not getting an output. The function stops when it gets a value that is FALSE and does not continue evaluation. This is much better than previously when we still got an output that was incorrect and did not get any warning. This also means that this passed the assertions for being a character vector and being in the Taxa in our dataset without error.
mean_func("Bacilllli")
Error: Bacilllli is not a Taxa in the dataset.
mean_func()
Error in eval(assertion, env) :
argument "Taxa" is missing, with no default
Most of these error messages are informative, but maybe not as explicit as they could be. Let’s customize our messages.
mean_func <- function(Taxa) {
#assertions to test input
assert_that(is.character(Taxa), msg = paste("Taxa is expected to be of type character. You have input type", typeof(Taxa), "."))
assert_that(Taxa %in% dat$Taxa, msg = paste(Taxa, "is not a Taxa in the dataset."))
assert_that(length(Taxa) == 1, msg = "Please input only one Taxa.")
summary <- filter(dat, Taxa == Taxa) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
print(paste('The mean for', Taxa,'is', round(summary$mean,2), "."))
return(summary)
}
We could be even more helpful and show the option of all Taxa to choose from, or convert everything to lowercase to reduce the chance of errors from having a mix of uppercase and lowercase. These are things you should think about, especially if you are passing on your code or making a package yourself. The easier people can use your functions, the more adoptable they will be.
There are a few functions included in assertthat for commonly used assertions. is.string() looks for a character vector of length 1 (combining 2 of our above assertions). Similarily is.count() looks for a single positive integer. noNA() tests the assertion that there are no NAs (different from is.na() because assertions are TRUE). Remember that the order of your assertions will affect the error message you receive. For example, an NA is not a string, but this fact may be masked if the check for a string precedes the check for an NA since the function will stop at the first FALSE evaluation.
not_empty() tests that all dimensions are greater than 0.
Let’s change a few assertions to test for an empty function call, NA as an input, and the use of is.string().
mean_func <- function(Taxa) {
assert_that(not_empty(Taxa))
assert_that(noNA(Taxa))
assert_that(is.string(Taxa))
assert_that(Taxa %in% dat$Taxa, msg = paste(Taxa, "is not a Taxa in the dataset."))
summary <- filter(dat, Taxa == Taxa) %>%
summarize(mean = mean(OTUs), sd = sd(OTUs))
print(paste('The mean for', Taxa,'is', round(summary$mean,2)))
return(summary)
}
mean_func()
Error in dim(x) %||% length(x) :
argument "Taxa" is missing, with no default
We are told there is a missing value, but not where is is. (case for data frames?)
There are more of these helper functions to assert_that() which I encourage you to check out in the help menu.
Multiple Inputs
So far, we have seen functions that can be reused for different inputs. What if we want to run the function for many inputs? What if I want the function to be able to take 20 Taxa? We need to be able to iterate over multiple inputs, one at a time, in a controlled manner. Enter control statements. With control statements, an expression is set that if evaluated to be TRUE will continue proceed and if evaluated to be FALSE will not.
For loops will iterate through a specified number of items. Starting with a simple loop to print the numbers 1 through 10. This for statement will iterate through the vector of numbers 1:10. In this case i will be 1,2,3,4,5,6,7,8,9, then 10.
for (i in 1:10){
print(i)
}
[1] 1
[1] 2
[1] 3
[1] 4
[1] 5
[1] 6
[1] 7
[1] 8
[1] 9
[1] 10
Often, we want to iterate over the contents of a vector, rather than a number. This is really the same thing.
for (i in x){
print(i)
}
[1] "rabbit"
[1] "dog"
[1] "llama"
You can subset for the actual value.
for (i in 1:length(x)){
print(x[i])
}
[1] "rabbit"
[1] "dog"
[1] "llama"
Or use numbers.
for (i in 1:length(x)){
print(i)
}
[1] 1
[1] 2
[1] 3
Why use numbers?
for (i in 1:length(x)){
if (i < 3){
print(i)
}
}
[1] 1
[1] 2
for (i in dat$Taxa){
print(i)
break
}
[1] "Acidobacteria_Gp1"
for (i in 1:nrow(dat)){
print(dat[i,1])
break
}
[1] "Acidobacteria_Gp1"
mean_func(c("Clostridia", "Bacteroidia", "Bacilli"))
[1] "The mean for Clostridia is 3423.41"
[1] "The mean for Bacteroidia is 684.57"
[1] "The mean for Bacilli is 330.85"
mean_func <- function(x, funs = list(mean = mean, sd = sd)) {
dat <- filter(dat, Taxa == x)
lapply(funs, function(f) f(dat$OTUs))
return(dat)
}
load_packages(c("foreach","MASS","doParallel"))
Loading required package: iterators
Loading required package: parallel
generalize to country, bacteria, #otus above/below mean? ranking position?
Basic Syntax of Functions in R
- the structure of a function (formals, body, parent environment)
- understanding scoping
- naming conventions
- generalizing a function
- setting default values
- adding ‘…’
- the output of your function
- sourcing functions
- NAs in functions
- return
Control Structures - if else - for - while - repeat - break - next
Handling Errors
- stopifnot(), warning(), suppressWarnings(), tryCatch()
- if stop
Testing Arguments and Validity of your Function
- informal testing
- testthat() for formal unit testing
- assertthat
Upscaling with functions
functions can be:
- assigned in variables
- stored in lists
- passed as arguments to other functions
- created inside other functions
- output by other functions
DRY principle - Don’t Repeat Yourself
Challenge
Write a function to check that the packages c(“dplyr”, “readxml”, “tidyr”) are installed and to load the packages. The function should install the packages if they are not already installled. Write a warning if the package is not installed, but is being installed. Will this function work for other packages?
Challenge
Write a function to read all .csv files in a directory into R and save each of them in a separate data frame.
read_files(path, FALSE)
[[1]]
[[2]]
[[3]]
NA
Resources:
http://stat545.com/block011_write-your-own-function-01.html
http://stat545.com/block011_write-your-own-function-02.html
http://stat545.com/block011_write-your-own-function-03.html
http://mazamascience.com/WorkingWithData/?p=912 http://adv-r.had.co.nz/Functions.html
Post-Lesson Assessment
Your feedback is essential to help the next cohort of trainees. Please take a minute to complete the following short survey: https://www.surveymonkey.com/r/X5C2JGR
Thanks for coming!!!
LS0tCnRpdGxlOiAiTGVzc29uIDYgLSBTY2FsaW5nIHVwIHlvdXIgQW5hbHlzZXM6IFdyaXRpbmcgRnVuY3Rpb25zIGluIFIiCm91dHB1dDogCiAgaHRtbF9kb2N1bWVudDoKICAgICAgICAgIGtlZXBfbWQ6IHllcwogICAgICAgICAgdG9jOiBUUlVFCiAgICAgICAgICB0b2NfZGVwdGg6IDMKICBodG1sX25vdGVib29rOgogICAgICAgICAgdG9jOiBUUlVFCiAgICAgICAgICB0b2NfZGVwdGg6IDMKLS0tCioqKgoKIVtdKGltZy9jYXJ0b29uMzcucG5nKQoKCjwvYnI+CgojI0EgcXVpY2sgaW50cm8gdG8gdGhlIGludHJvIHRvIFIgTGVzc29uIFNlcmllcwoKPC9icj4KClRoaXMgJ0ludHJvIHRvIFIgTGVzc29uIFNlcmllcycgaXMgYnJvdWdodCB0byB5b3UgYnkgdGhlIENlbnRyZSBmb3IgdGhlIEFuYWx5c2lzIG9mIEdlbm9tZSBFdm9sdXRpb24gJiBGdW5jdGlvbidzIChDQUdFRikgYmlvaW5mb3JtYXRpY3MgdHJhaW5pbmcgaW5pdGlhdGl2ZS4gVGhpcyBjb3Vyc2Ugd2FzIGRldmVsb3BlZCBiYXNlZCBvbiBmZWVkYmFjayBvbiB0aGUgbmVlZHMgYW5kIGludGVyZXN0cyBvZiB0aGUgRGVwYXJ0bWVudCBvZiBDZWxsICYgU3lzdGVtcyBCaW9sb2d5IGFuZCB0aGUgRGVwYXJ0bWVudCBvZiBFY29sb2d5IGFuZCBFdm9sdXRpb25hcnkgQmlvbG9neS4gCgoKVGhpcyBsZXNzb24gaXMgdGhlIGxhc3QgaW4gYSA2LXBhcnQgc2VyaWVzLiBIb3BlZnVsbHkgeW91IGhhdmUgbGVhcm5lZCBhIGJpdCBhYm91dCBob3cgdG8gaW1wb3J0IGFuZCBtYW5pcHVsYXRlIGRhdGEsIG1ha2UgZXhwbG9yYXRvcnkgcGxvdHMsIHBlcmZvcm0gc29tZSBiYXNpYyBzdGF0aXN0aWNhbCB0ZXN0cywgdGVzdCBhIHJlZ3Jlc3Npb24gbW9kZWwsIGFuZCBtYWtlIHNvbWUgZXZlbiBwcmV0dGllciBwbG90cyBhbmQgZG9jdW1lbnRzIHRvIHNoYXJlIHRvIHJlc3VsdHMuIAoKCiFbXShpbWcvZGF0YS1zY2llbmNlLWV4cGxvcmUucG5nKQoKPC9icj4KClRvZGF5IHdlIGFyZSBnb2luZyB0byBsZWFybiB0byB3cml0ZSBmdW5jdGlvbnMsIHdoaWNoIGNhbiBzYXZlIHlvdSB0aW1lIGFuZCBoZWxwIHNjYWxlIHVwIHlvdXIgYW5hbHlzZXMuIEl0IHdpbGwgYWxzbyBoZWxwIHRvIG1pbmltaXplIGNvcHkgYW5kIHBhc3RpbmcsIHJlZHVuZGFuY3ksIGFuZCBmZWVsaW5nIGxpa2UgYSBuZXdiLgoKCiFbXShpbWcvc3BvdGlmeS1ob3d0b2J1aWxkbXZwLmdpZikKCjwvYnI+CgpUaGUgc3RydWN0dXJlIG9mIHRoZSBjbGFzcyBpcyBhIGNvZGUtYWxvbmcgc3R5bGUuIEl0IGlzIGhhbmRzIG9uLiBUaGUgbGVjdHVyZSBBTkQgY29kZSB3ZSBhcmUgZ29pbmcgdGhyb3VnaCBhcmUgYXZhaWxhYmxlIG9uIEdpdEh1YiBmb3IgZG93bmxvYWQgYXQgPGh0dHBzOi8vZ2l0aHViLmNvbS9lYWN0b24vQ0FHRUY+LCBzbyB5b3UgY2FuIHNwZW5kIHRoZSB0aW1lIGNvZGluZyBhbmQgbm90IHRha2luZyBub3Rlcy4gQXMgd2UgZ28gYWxvbmcsIHRoZXJlIHdpbGwgYmUgc29tZSBjaGFsbGVuZ2UgcXVlc3Rpb25zIGFuZCBtdWx0aXBsZSBjaG9pY2UgcXVlc3Rpb25zIG9uIFNvY3JhdGl2ZS4gQXQgdGhlIGVuZCBvZiB0aGUgY2xhc3MgaWYgeW91IGNvdWxkIHBsZWFzZSBmaWxsIG91dCBhIHBvc3QtbGVzc29uIHN1cnZleSAoPGh0dHBzOi8vd3d3LnN1cnZleW1vbmtleS5jb20vci9YNUMySkdSPiksIGl0IHdpbGwgaGVscCBtZSBmdXJ0aGVyIGRldmVsb3AgdGhpcyBjb3Vyc2UgYW5kIHdvdWxkIGJlIGdyZWF0bHkgYXBwcmVjaWF0ZWQuIAoKKioqCgojIyMjUGFja2FnZXMgVXNlZCBpbiBUaGlzIExlc3NvbgoKVGhlIGZvbGxvd2luZyBwYWNrYWdlcyBhcmUgdXNlZCBpbiB0aGlzIGxlc3NvbjoKCmB0aWR5dmVyc2VgIChgZ2dwbG90MmAsIGB0aWR5cmAsIGBkcGx5cmApICAgICAKYHRlc3R0aGF0YApgYXNzZXJ0dGhhdGAKClBsZWFzZSBpbnN0YWxsIGFuZCBsb2FkIHRoZXNlIHBhY2thZ2VzIGZvciB0aGUgbGVzc29uLiBJbiB0aGlzIGRvY3VtZW50IEkgd2lsbCBsb2FkIGVhY2ggcGFja2FnZSBzZXBhcmF0ZWx5LCBidXQgSSB3aWxsIG5vdCBiZSByZW1pbmRpbmcgeW91IHRvIGluc3RhbGwgdGhlIHBhY2thZ2UuIFJlbWVtYmVyOiB0aGVzZSBwYWNrYWdlcyBtYXkgYmUgZnJvbSBDUkFOIE9SIEJpb2NvbmR1Y3Rvci4gCgoKKioqCiMjIyNIaWdobGlnaHRpbmcKCmBncmV5IGJhY2tncm91bmRgIC0gYSBwYWNrYWdlLCBmdW5jdGlvbiwgY29kZSBvciBjb21tYW5kICAgICAgCippdGFsaWNzKiAtIGFuIGltcG9ydGFudCB0ZXJtIG9yIGNvbmNlcHQgICAgIAoqKmJvbGQqKiAtIGhlYWRpbmcgb3IgYSB0ZXJtIHRoYXQgaXMgYmVpbmcgZGVmaW5lZCAgICAgIAo8c3BhbiBzdHlsZT0iY29sb3I6Ymx1ZSI+Ymx1ZSB0ZXh0PC9zcGFuPiAtIG5hbWVkIG9yIHVubmFtZWQgaHlwZXJsaW5rICAgICAKCgoqKioKX19PYmplY3RpdmU6X18gQXQgdGhlIGVuZCBvZiB0aGlzIHNlc3Npb24geW91IHdpbGwgYmUgYWJsZSB0byB3cml0ZSBmdW5jdGlvbnMsIG1ha2luZyB5b3VyIGNvZGluZyBtb3JlIGVmZmljaWVudCwgdW5kZXJzdGFuZGFibGUsIHJlcHJvZHVjaWJsZSwgYW5kIGhvcGVmdWxseSBsZXNzIGZydXN0cmF0aW5nLgoKCkxvYWQgdGhlIHBhY2thZ2VzIQoKYGBge3Igd2FybmluZyA9IEZBTFNFLCBtZXNzYWdlID0gRkFMU0V9CmxpYnJhcnkodGlkeXZlcnNlKQpsaWJyYXJ5KHRlc3R0aGF0KQpsaWJyYXJ5KGFzc2VydHIpCmBgYAoKIyMjRGF0YXNldDogUHlyb3NlcXVlbmNpbmcgb2YgdGhlIFYzLVY1IGh5cGVydmFyaWFibGUgcmVnaW9ucyBvZiB0aGUgMTZTIHJSTkEgZ2VuZQoKMTZTIHJSTkEgcHlyb3NlcXVlbmNpbmcgb2YgMzAgbGF0cmluZXMgZnJvbSBUYW56YW5pYSBhbmQgVmlldG5hbSBhdCBkaWZmZXJlbnQgZGVwdGhzIChtdWx0aXBsZXMgb2YgMjBjbSkuIE1pY3JvYmlhbCBhYnVuZGFuY2UgaXMgcmVwcmVzZW50ZWQgaW4gT3BlcmF0aW9uYWwgVGF4b25vbWljIFVuaXRzIChPVFVzKS4gT3BlcmF0aW9uYWwgVGF4b25vbWljIFVuaXRzIChPVFVzKSBhcmUgZ3JvdXBzIG9mIG9yZ2FuaXNtcyBkZWZpbmVkIGJ5IGEgc3BlY2lmaWVkIGxldmVsIG9mIEROQSBzZXF1ZW5jZSBzaW1pbGFyaXR5IGF0IGEgbWFya2VyIGdlbmUgKGUuZy4gOTclIHNpbWlsYXJpdHkgYXQgdGhlIFY0IGh5cGVydmFyaWFibGUgcmVnaW9uIG9mIHRoZSAxNlMgclJOQSBnZW5lKS4gSW50cmluc2ljIGVudmlyb25tZW50YWwgZmFjdG9ycyBzdWNoIGFzIHBILCB0ZW1wZXJhdHVyZSwgb3JnYW5pYyBtYXR0ZXIgY29tcG9zaXRpb24gd2VyZSBhbHNvIHJlY29yZGVkLgoKV2UgaGF2ZSAyIGNzdiBmaWxlczoKCjEuIEEgbWV0YWRhdGEgZmlsZSAoTmFtaW5nIGNvbnZlbnRpb25zOiBbQ291bnRyeV9MYXRyaW5lTm9fRGVwdGhdKSB3aXRoIHNhbXBsZSBuYW1lcyBhbmQgZW52aXJvbm1lbnRhbCB2YXJpYWJsZXMuICAgICAKMi4gT1RVIGFidW5kYW5jZSB0YWJsZS4KCkIgVG9yb25kZWwsIEpISiBFbnNpbmssIE8gR3VudmlydXNkdSwgVVogSWpheiwgSiBQYXJraGlsbCwgRiBBYmRlbGFoaSwgVi1BIE5ndXllbiwgUyBTdWRnZW4sIFcgR2lic29uLCBBVyBXYWxrZXIsIGFuZCBDIFF1aW5jZS4KQXNzZXNzbWVudCBvZiB0aGUgaW5mbHVlbmNlIG9mIGludHJpbnNpYyBlbnZpcm9ubWVudGFsIGFuZCBnZW9ncmFwaGljYWwgZmFjdG9ycyBvbiB0aGUgYmFjdGVyaWFsIGVjb2xvZ3kgb2YgcGl0IGxhdHJpbmVzCk1pY3JvYmlhbCBCaW90ZWNobm9sb2d5LCA5KDIpOjIwOS0yMjMsIDIwMTYuIERPSToxMC4xMTExLzE3NTEtNzkxNS4xMjMzNAoKKioqCgoKIyNXaHkgRnVuY3Rpb25zPwoKCgpgYGB7cn0KZGF0IDwtIHJlYWQuY3N2KCJkYXRhL2xvbmdfU1BFX3BpdGxhdHJpbmUuY3N2IiwgaGVhZGVyID0gVCwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgpgYGAKCkkgd2FudCB0byBrbm93IHRoZSBtZWRpYW4gYW5kIG1lZGlhbiBhYnNvbHV0ZSBkZXZpYXRpb24gb2YgT1RVcyBpbiBhbGwgb2Ygb3VyIHNhbXBsZXMgZm9yIENsb3N0cmlkYS4KCmBgYHtyfQpkYXQgJT4lIGZpbHRlcihUYXhhID09ICJDbG9zdHJpZGlhIikgJT4lIHN1bW1hcml6ZShtZWFuID0gbWVkaWFuKE9UVXMpLCBtYWQgPSBtYWQoT1RVcykpCgpgYGAKR3JlYXQuIFdoYXQgYWJvdXQgdGhlIG1lZGlhbiBhbmQgbWVkaWFuIGFic29sdXRlIGRldmlhdGlvbiBmb3IgQmFjaWxsaT8gSG1tbW0uIEkgZ3Vlc3MgSSdsbCBqdXN0IGNvcHkgYW5kIHBhc3RlIHRoZSBhYm92ZSBjb2RlIGJlY2F1c2UgaXQgd2lsbCB0YWtlIDIgc2Vjb25kcy4KCmBgYHtyfQpkYXQgJT4lIGZpbHRlcihUYXhhID09ICJCYWNpbGxpIikgJT4lIHN1bW1hcml6ZShtZWFuID0gbWVkaWFuKE9UVXMpLCBtYWQgPSBtYWQoT1RVcykpCmBgYApNYXliZSBJJ2xsIGNoZWNrIG91dCBCYWN0ZXJvaWRpYSwgdG9vLi4uIGJ5IGNvcHkgYW5kIHBhc3RpbmcgQUdBSU4uCgpgYGB7cn0KZGF0ICU+JSBmaWx0ZXIoVGF4YSA9PSAiQmFjdGVyb2lkaWEiKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWRpYW4oT1RVcyksIG1hZCA9IG1hZChPVFVzKSkKYGBgCgpDb21lIHRvIHRoaW5rIG9mIGl0LCB0aGVyZSBhcmUgYSBsb3Qgb2YgemVyb3MgaW4gdGhpcyBkYXRhLiBNYXliZSBJIHNob3VsZCBiZSBsb29raW5nIGF0IG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBpbnN0ZWFkIG9mIG1lZGlhbiBhbmQgbWFkLiBJJ2xsIGp1c3QgZ28gYmFjayBhbmQgY2hhbmdlIHRoaXMgaW4gZWFjaCBvZiBteSAzIGxpbmVzIG9mIGNvZGUuCgoKYGBge3J9CmRhdCAlPiUgZmlsdGVyKFRheGEgPT0gIkNsb3N0cmlkaWEiKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQpkYXQgJT4lIGZpbHRlcihUYXhhID09ICJCYWNpbGxpIikgJT4lIHN1bW1hcml6ZShtZWFuID0gbWVhbihPVFVzKSwgc2QgPSBzZChPVFVzKSkKZGF0ICU+JSBmaWx0ZXIoVGF4YSA9PSAiQmFjdGVyb2lkaWEiKSAlPiUgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQpgYGAKCgoKRG9lcyBhbnkgb2YgdGhpcyBmZWVsIGZhbWlsaWFyPz8KCkZ1bmN0aW9ucyBhbGxvdyB1cyB0bzoKCi0gYXZvaWQgcmVwZXRpdGlvbiwgbWFrZSBvdXIgY29kZSBtb3JlIGNvbXBhY3QKLSBhdm9pZCBlcnJvcnMsIGNvcHkvcGFzdGUgZXJyb3JzLCB0eXBvcwotIHZlcnNhdGlsZSwgdXBkYXRlYWJsZQotIHVwc2NhbGUsIGdlbmVyYWxpemFibGUKLSByZXN1YWJsZSwgZXZlbiBmb3Igb3RoZXIgcHJvamVjdHMhCgojI1RoZSBTdHJ1Y3R1cmUgb2YgYSBGdW5jdGlvbgoKQSBmdW5jdGlvbiBoYXMgMyBwYXJ0czogCgotIF9mb3JtYWxzXyBhcmUgdGhlIGlucHV0KHMpIG9mIHRoZSBmdW5jdGlvbiwgCi0gdGhlIF9ib2R5XyBpcyB0aGUgYnJhY2VkIGV4cHJlc3Npb24sIAotIGFuZCB0aGUgX2Vudmlyb25tZW50XyBpcyB0aGUgZW5jbG9zdXJlIGluIHdoaWNoIHRoZSBmdW5jdGlvbiB3YXMgZGVmaW5lZC4gCgpXZSBjYW4gbG9vayBhdCB0aGVzZSBmb3IgYSBmdW5jdGlvbiB3ZSBhcmUgZmFtaWxpYXIgd2l0aCwgYHJlYWQuY3N2YC4gRWFjaCBhcmd1bWVudCBpcyBzcGVjaWZpZWQgd2l0aCBgJGAgYW5kIGFueSBkZWZhdWx0IHZhbHVlcyBhcmUgbGlzdGVkIGBbMV1gLiBUbyB1c2UgYHJlYWQuY3N2YCB5b3UgbXVzdCBpbnB1dCBhIGZpbGUgLSB0aGlzIGFyZ3VtZW50IGhhcyBubyBkZWZhdWx0IHZhbHVlLiBUaGUgcmVzdCBvZiB0aGUgYXJndW1lbnRzIGhhdmUgYSBkZWZhdWx0IHZhbHVlIChzdWNoIGFzIGBoZWFkZXIgPSBUUlVFYCksIGJ1dCBjYW4gYmUgbW9kaWZpZWQgYnkgc3BlY2lmeWluZyB0aGUgYXJndW1lbnQgKGJ5IGlucHV0dGluZyBgaGVhZGVyID0gRkFMU0VgKS4gTW9zdCBmdW5jdGlvbiBkZWZhdWx0cyBhcyAnc2Vuc2libGUnLCBidXQgaWYgeW91IGhhdmUgbm90IHVzZWQgdGhlIGZ1bmN0aW9uIGJlZm9yZSBpdCBpcyB3b3J0aCByZWFkaW5nIGFib3V0IHRoZSBhcmd1bWVudHMgaW4gdGhlIGhlbHAgbWVudS4gRVNQRUNJQUxMWSBkbyB0aGlzIHdpdGggbWF0aGVtYXRpY2FsIGZ1bmN0aW9ucyB0byBzZWUgd2hhdCBhc3N1bXB0aW9ucyB5b3UgYXJlIG1ha2luZyBieSB1c2luZyB0aGUgZGVmYXVsdCBwYXJhbWV0ZXJzLiBgcmVhZC5jc3ZgIGFsc28gaGFzIHRoZSBtYWdpY2FsIGAuLi5gIGFyZ3VtZW50LiBUaGlzIGFyZ3VtZW50IGFsbG93cyB5b3UgdG8gcGFzcyBhcmJpdHJhcnkgYXJndW1lbnRzIHRvIGFub3RoZXIgZnVuY3Rpb24uIFdlIHdpbGwgc2VlIHRoaXMgaW4gdXNlIHNob3J0bHkuCgoKYGBge3J9CmZvcm1hbHMocmVhZC5jc3YpCmBgYAoKVGhlIGJvZHkgb2YgYHJlYWQuY3N2YCBsb29rcyBzdXNwaWNpb3VzbHkgbGlrZSB0aGUgZnVuY3Rpb24gYHJlYWQudGFibGVgIGJlY2F1c2UgaXQgaXMsIGluZGVlZCBhICd3cmFwcGVyJyBmdW5jdGlvbiB3aGljaCB1c2VzIGFuIGV4aXN0aW5nIGZ1bmN0aW9uIHdpdGggZGlmZmVyZW50IHZhbHVlcyBmb3IgZGVmYXVsdCBhcmd1bWVudHMuCgpgYGB7cn0KYm9keShyZWFkLmNzdikKYGBgClRoZSBlbnZpcm9ubWVudCBmb3IgYHJlYWQuY3N2YCBpcyB0aGUgdXRpbHMgcGFja2FnZS4gV2hlbiB3ZSBtYWtlIGEgZnVuY3Rpb24sIGl0IHdpbGwgYmUgY3JlYXRlZCBpbiAnLkdsb2JhbEVudicgdGhlICdnbG9iYWwgZW52aXJvbm1lbnQnLiBUaGUgZ2xvYmFsIGVudmlyb25tZW50IGlzIG91ciBpbnRlcmFjdGl2ZSB3b3Jrc3BhY2UsIHZpZXdlZCBpbiB0aGUgZW52aXJvbm1lbnQgd2luZG93IGluIHRoZSB0b3AgcmlnaHQsIGFuZCBjYW4gYmUgdGhvdWdodCBvZiBhcyBhIGJhZyB0aGF0IGhvbGRzIGFsbCBvZiBvdXIgdmFyaWFibGVzIGFuZCBmdW5jdGlvbnMuIE5vdGUgdGhhdCB3ZSBjYW4gdXNlIHRoZSBkcm9wZG93biBtZW51IHRvIGNoYW5nZSB0aGUgZW52aXJvbm1lbnQgdG8gcGFja2FnZTp1dGlscywgYW5kIHdlIGNhbiBmaW5kIGByZWFkLmNzdmAgaW4gdGhhdCBlbnZpcm9ubWVudC4KIApgYGB7cn0KZW52aXJvbm1lbnQocmVhZC5jc3YpCmBgYAoKIyNXcml0aW5nIEZ1bmN0aW9ucwoKClRoZSBiYXNpYyBmb3JtIG9mIGEgZnVuY3Rpb24gdGFrZXMgaW4gYXJndW1lbnRzIChzcGVjaWZpY2F0aW9ucyBieSB0aGUgdXNlciksIGhhcyBzdGF0ZW1lbnRzIG9yIGV4cHJlc3Npb25zICh3aGF0IHRoZSBmdW5jdGlvbiBkb2VzKSwgYW5kIHJldHVybnMgYW4gb2JqZWN0LiBBIGZ1bmN0aW9uIGRvZXMgbm90IG5lZWQgdG8gYmUgbmFtZWQgdG8gYmUgdXNlZCwgaG93ZXZlciwgdGhlIGdyZWF0ZXN0IHV0aWxpdHkgY29tZXMgZnJvbSByZXVzaW5nIGZ1bmN0aW9ucy4gQSBuYW1lZCBmdW5jdGlvbiBpcyBjYWxsZWQgdXNpbmcgcGFyZW50aGVzZXM6IGBuYW1lZF9mdW5jdGlvbihhcmd1bWVudHMpYC4KCkRpZmZlcmVudCBmcm9tIG90aGVyIHByb2dyYW1taW5nIGxhbmd1YWdlcywgUiBpcyBub3QgcGlja3kgYWJvdXQgd2hpdGVzcGFjZSAodGhlIGFycmFuZ2VtZW50IG9mIHRhYnMsIHNwYWNlcywgaW5kZW50cykuIEhvd2V2ZXIgZm9ybWF0dGluZyB5b3VyIGZ1bmN0aW9uIGFzIGJlbG93IChzdGF0ZW1lbnRzIGluZGVudGVkIGJlbG93IHlvdXIgYXJndW1lbnRzKSB3aWxsIGhlbHAgdG8gbWFrZSBpdCByZWFkYWJsZSBhbmQgZmFtaWxpYXIgdG8gb3RoZXIgbGFuZ3VhZ2VzLiAKClRoaXMgbWF5IGJlIGEgZ29vZCB0aW1lIHRvIHBvaW50IG91dCB0aGF0IHBsYWNpbmcgeW91ciBjdXJzb3IgYmVzaWRlIGEgY2xvc2luZyBicmFja2V0IHdpbGwgaGlnaGxpZ2h0IGl0cyBjb3JyZXNwb25kaW5nIG9wZW5pbmcgYnJhY2tldC4gVGhpcyBpcyByZWFsbHkgdXNlZnVsIGFzIHlvdXIgY29kZSBnZXRzIG1vcmUgY29tcGxleC4gQWxzbywgdHJ5IGRlbGV0aW5nIHRoZSBjbG9zaW5nIGJyYWNrZXQgYmVzaWRlICdvYmplY3QnLiBZb3Ugd2lsbCBzZWUgdGhhdCBSIGlzIGdpdmluZyB5b3UgYSBzZXJpZXMgb2Ygd2FybmluZ3MgKGJyYWNrZXRzIGhpZ2hsaWdodGVkIGluIHJlZCwgYW5kICd4JyBiZXNpZGUgMyBsaW5lcyBvZiBjb2RlKS4gQWdhaW4sIHRoaXMgaXMgYSBoZWxwZnVsIHdhcm5pbmcgdGhhdCBzYXZlcyB5b3UgdGltZSBmcm9tIHJ1bm5pbmcgYW4gaW5jb21wbGV0ZSBmdW5jdGlvbi4KCmBgYHtyfQpuYW1lZF9mdW5jdGlvbiA8LSAKICBmdW5jdGlvbihhcmd1bWVudHMpIHsKICAgICAgc3RhdG1lbnRzCiAgICAgIHJldHVybihvYmplY3QpIAogIH0KYGBgCgoKV2Ugd2FudCB0byB3cml0ZSBhIGZ1bmN0aW9uIHRoYXQgd2lsbCBjYWxjdWxhdGUgdGhlIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBmb3IgYSBnaXZlbiBUYXhhLiAKCldlIGNhbiBzdGFydCBieSBuYW1pbmcgb3VyIGZ1bmN0aW9uIHNvbWV0aGluZyB2YWd1ZWx5IGluZm9ybWF0aXZlLiAoVGVjaG5pY2FsbHksIHlvdSBjYW4gbmFtZSB5b3VyIGZ1bmN0aW9ucyBhbmQgdmFyaWFibGVzIHdoYXRldmVyIHlvdSB3YW50LiBJIGNvdWxkIG5hbWUgdGhlbSBhZnRlciBBdmVuZ2VycyBjaGFyYWN0ZXJzLCBidXQgcGVvcGxlIChvciBteXNlbGYgaW4gMyBtb250aHMpIHdvdWxkIGhhdmUgdG8gd29yayBwcmV0dHkgaGFyZCB0byBmaWd1cmUgb3V0IHdoYXQgSSB3YXMgZG9pbmcuIFJlbWVtYmVyLCB3aXRoIGdyZWF0IHBvd2VyIGNvbWVzIGdyZWF0IHJlc3BvbnNpYmlsaXR5KS4gCgpPdXIgZnVuY3Rpb24gaXMgZ29pbmcgdG8gdGFrZSAnVGF4YScgYXMgYW4gYXJndW1lbnQuIEZvciBzb21lIG9mIHlvdSwgd2hlbiB5b3UgdHlwZSAneycgUiB3aWxsIGF1dG9tYXRpY2FsbHkgZ2l2ZSB5b3UgeW91ciBjbG9zaW5nIGJyYWNrZXQgJ30nIHNvIHlvdSB3b24ndCBmb3JnZXQgLSBldmVyIHRyeWluZyB0byBiZSBoZWxwZnVsLCB0aGFua3MgUi4gTmV4dCB3ZSBjYW4gdGFrZSBvdXIgcHJldmlvdXMgY29kZSwgYW5kIHNhdmUgdGhlIG91dHB1dCBpbnRvIGFuIG9iamVjdCBjYWxsZWQgJ3N1bW1hcnlfZGF0Jy4gVGhpcyBpcyBiZWNhdXNlIEkgZG9uJ3Qgd2FudCB0byBvdmVyd3JpdGUgbXkgb3JpZ2luYWwgZGF0YSBmcmFtZSBldmVyeSB0aW1lIEkgcnVuIHRoZSBmdW5jdGlvbi4gV2UgcmVwbGFjZSBvdXIgc3BlY2lmaWMgVGF4YSwgJ0Nsb3N0cmlkaWEnLCB3aXRoIG91ciBhcmd1bWVudCwgJ1RheGEnLiBUaGlzIGlzIG91ciBleHByZXNzaW9uLiBBbGwgd2UgaGF2ZSB0byBkbyBub3cgaXMgYHJldHVybmAgb3VyIGRhdGEgZnJhbWUgYW5kIG1ha2Ugc3VyZSBhbGwgb2Ygb3VyIGJyYWNrZXRzIG1hdGNoLgoKVm9pbGEhIAoKYGBge3J9Cm1lYW5fZnVuYyA8LSAKICBmdW5jdGlvbihUYXhhKSB7CiAgICBzdW1tYXJ5X2RhdCA8LSBmaWx0ZXIoZGF0LCBUYXhhID09IFRheGEpICU+JSAKICAgIHN1bW1hcml6ZShtZWFuID0gbWVhbihPVFVzKSwgc2QgPSBzZChPVFVzKSkKICAgIHJldHVybihzdW1tYXJ5X2RhdCkKfQpgYGAKCldoZW4geW91IHJ1biB0aGlzIGZ1bmN0aW9uLCBpdCB3aWxsIGFwcGVhciwgbmFtZWQgYW5kIHdpdGggaXRzIGFyZ3VtZW50cywgaW4gdGhlIGdsb2JhbCBlbnZpcm9ubWVudC4gSWYgeW91IGNsaWNrIG9uIHRoZSB3aGl0ZSBib3ggdG8gdGhlIHJpZ2h0IG9mIHRoZSBmdW5jdGlvbiwgeW91IGNhbiBzZWUgdGhlIGNvZGUgZm9yIHlvdXIgZnVuY3Rpb24uIFlvdSBjYW4gbm93IHVzZSB5b3VyIGZ1bmN0aW9uLiAKCmBgYHtyfQptZWFuX2Z1bmMoIkNsb3N0cmlkaWEiKQptZWFuX2Z1bmMoIkJhY2lsbGkiKQptZWFuX2Z1bmMoIkJhY3RlcmlvZGlhIikKYGBgCmBgYHtyfQpvdXQgPC0gbWVhbl9mdW5jKCJCYWN0ZXJpb2RpYSIpCmBgYAoKCmBgYHtyfQptZWFuX2Z1bmMgPC0gCiAgZnVuY3Rpb24oVGF4YSkgewogICAgZmlsdGVyKGRhdCwgVGF4YSA9PSBUYXhhKSAlPiUgCiAgICBzdW1tYXJpemUobWVhbiA9IG1lYW4oT1RVcyksIHNkID0gc2QoT1RVcykpCiAgICBwcmludChwYXN0ZSgnVGhpcyBpcyB0aGUgbWVhbiBmb3InLCBUYXhhKSkKfQpgYGAKCiMjVW5kZXJzdGFuZGluZyBMZXhpY2FsIFNjb3BpbmcgCgotIHdoeSBkb2VzIGRhdCBnZXQgY2hhbmdlZCwgYnV0IHN1bW1hcnlfZGF0IGRvZXMgbm90IGVuZCB1cCBiZWluZyBhIGdsb2JhbCBlbnZpcm9ubWVudGFsIHZhcmlhYmxlIHdoZW4gcmV0dXJuZWQ/CgoKLW1heSB3YW50IHRvIGJlIGFibGUgdG8gdGFrZSBpbnRvIGRpZmZlcmVudCBkYXRhIGZyYW1lcy4uLgoKCmBgYHtyfQptZWFuX2Z1bmMgPC0gZnVuY3Rpb24oZGF0LCB4KSB7CiAgZGF0IDwtIGZpbHRlcihkYXQsIFRheGEgPT0geCkgJT4lIAogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQp9CgptZWFuX2Z1bmMoZGF0LCAiQ2xvc3RyaWRpYSIpCmBgYAoKCgoKQWxsb3dzIHVzIHRvIHZhcnkgdGhlIFRheGEgYW5kIHRoZSBmdW5jdGlvbnMuCgpgYGB7cn0KbWVhbl9mdW5jIDwtIGZ1bmN0aW9uKHgsIGZ1bnMgPSBsaXN0KG1lYW4gPSBtZWFuLCBzZCA9IHNkKSkgewogIGRhdCA8LSBmaWx0ZXIoZGF0LCBUYXhhID09IHgpIAogIGxhcHBseShmdW5zLCBmdW5jdGlvbihmKSBmKGRhdCRPVFVzKSkKfQoKbWVhbl9mdW5jKCJDbG9zdHJpZGlhIiwgZnVucyA9IGxpc3QobWVhbiA9IG1lYW4sIG1lZGlhbiA9IG1lZGlhbiwgc2QgPSBzZCkpCgoKYGBgCgojI0RlZmVuc2l2ZSBQcm9ncmFtbWluZwoKCiMjI1Rlc3RpbmcgQXJndW1lbnRzIGFuZCBWYWxpZGl0eSBvZiB5b3VyIEZ1bmN0aW9uCgpZb3UgbWlnaHQgdGhpbmsgdGhhdCB5b3UgYXJlIGRvbmUgd2l0aCB0aGlzIGZ1bmN0aW9uLiBZb3UgaGF2ZSB0aGUgYW5zd2VycywgcmlnaHQ/IEJ1dCBmdW5jdGlvbnMgYXJlIG5vdCBmb29scHJvb2YuCgpgYGB7cn0KbWVhbl9mdW5jKCJDbG9zdHJpZGlhIiwgZnVucyA9IGxpc3QobWVhbiA9IHF1YW50aWxlLCBtZWRpYW4gPSBtZWRpYW4sIHNkID0gbWVhbikpCmBgYAoKSG1tbS4uLiBtaXNsYWJlbGxlZCBudW1iZXJzIGNvdWxkIGdldCBhIGxpdHRsZSBjb25mdXNpbmcuIAoKV2hhdCBpZiBJIHdhbnRlZCB0aGUgc3VtbWFyeSBzdGF0aXN0aWNzIGZvciAzIFRheGEgYXQgb25jZT8KCmBgYHtyfQptZWFuX2Z1bmMoYygiQ2xvc3RyaWRpYSIsICJCYWN0ZXJpb2RpYSIsICJCYWNpbGxpIikpCmBgYApVaCBvaC4gVGhhdCdzIGJhZC4gSSBkaWRuJ3Qgd2FudCB0byBtZXJnZSB0aGUgMyBUYXhhLi4uIG9yIGRpZCBpdCB0YWtlIHRoZSBsYXN0IHZhbHVlPwoKWW91ciBuZXh0IHN0ZXAgaXMgdG8gdHJ5IGFuZCBpbnRlbnRpb25hbGx5IGJyZWFrIHlvdXIgZnVuY3Rpb24uIEdpdmUgaXQgaW5wdXQgdGhhdCB3YXNuJ3QgbWVhbnQgZm9yIGl0IGFuZCBzZWUgd2hhdCBoYXBwZW5zLiBUcm91YmxlLXNob290aW5nIHRoaXMgd2F5IGlzIGNhbGxlZCAnZGVmZW5zaXZlIHByb2dyYW1taW5nJy4gV2hhdCB3ZSBhcmUgZ29pbmcgdG8gZG8gaXMgaW1wbGVtZW50IGNoZWNrcyBhbmQgYmFsYW5jZXMgc3VjaCB0aGF0IG91ciBmdW5jdGlvbiBjYW4ndCBkbyBhbnl0aGluZyB3ZWlyZC4gCgpJbml0aWFsIGNoZWNrcyBjYW4gYmUgdGhpbmdzIGxpa2UgLSB3aGF0IGhhcHBlbnMgd2hlbiBJIGdpdmUgYW4gaW5wdXQgdGhhdCBpcyBudW1lcmljIHdoZW4gSSBhbSBleHBlY3RpbmcgY2hhcmFjdGVyIGRhdGE/IFdoYXQgaGFwcGVucyBpZiB0aGVyZSBpcyBhbiBOQSBpbiB0aGUgZGF0YXNldD8gV2hhdCBoYXBwZW5zIGlmIHRoZXJlIGlzIGEgdHlwbz8gV2hhdCBoYXBwZW5zIGlmIEkgYW0gZ2l2ZW4gYW4gdW5leHBlY3RlZCBkYXRhIHN0cnVjdHVyZT8gV2hhdCBoYXBwZW5zIGlmIHRoZXJlIGlzIG1vcmUgb3IgbGVzcyBkYXRhIHRoYW4gSSBhbSBleHBlY3Rpbmc/IFdoYXQgaGFwcGVucyB3aGVuIEkgaW5wdXQgbG9naWNhbCBkYXRhPwoKYGBge3J9Cm1lYW5fZnVuYygiQmFjaWxsbGxpIikKYGBgCmBgYHtyfQptZWFuX2Z1bmMoTkEpCmBgYApgYGB7cn0KbWVhbl9mdW5jKDIxMCkKYGBgCmBgYHtyfQptZWFuX2Z1bmMoKQpgYGAKCiMjI0Vycm9yIEhhbmRsaW5nCgoKIyMjVW5pdCBUZXN0cwoKLXRlc3R0aGF0IGFzc3VtcHRpb24gZm9yIGZ1bmN0aW9uIG5hbWUgPSBuYW1lPwoKUmV0dXJucyB0aGUgbGFzdCBsaW5lLi4uIHVubGVzcyBleHBsaWNpdGx5IGNhbGxlZCB3aXRoIGByZXR1cm5gCgoKYGBge3J9CnRlc3RfdGhhdCgiaW5wdXQgaXMgVGF4YSIsIHsKICBleHBlY3Rfc3VjY2VzcyhleHBlY3RfdHlwZShtZWFuX2Z1bmMoIkJhY2lsbGkiKSwgdHlwZSA9ICJjaGFyYWN0ZXIiKSkKICBleHBlY3RfZmFpbHVyZShleHBlY3RfdHlwZShtZWFuX2Z1bmMoMSksIHR5cGUgPSAiY2hhcmFjdGVyIikpCn0pCmBgYAoKIyMjQXNzZXJ0aW9ucwoKSW4gYSBwZXJmZWN0IHdvcmxkLCBpZiBzb21ldGhpbmcgZ29lcyB3cm9uZyB3aGlsZSB5b3UgYXJlIGNvZGluZywgeW91IHdhbnQgYW4gaW5mb3JtYXRpdmUgZXJyb3IgbWVzc2FnZSB0byByZWR1Y2UgdGltZSB0cm91YmxlLXNob290aW5nLiBXZSBhbHNvIHdhbnQgdG8gZG8gdGhhdCBmb3Igb3RoZXIgcGVvcGxlLiBBZ2FpbiwgSGFkbGV5IGhhcyBjb21lIHRvIHRoZSByZXNjdWUgd2l0aCBhIHdheSB0byB0ZXN0IGFzc2VydGlvbnMgYW5kIGdpdmUgbWVhbmluZ2Z1bCBlcnJvciBtZXNzYWdlcyB3aXRoIHRoZSBwYWNrYWdlIGBhc3NlcnR0aGF0YC4gCgpfX0Fzc2VydGlvbnNfXyBhcmUgc3RhdGVtZW50cyB0aGF0IGFyZSBrbm93biB0byBiZSBUUlVFLCBidXQgd2UgdXNlIHdoZXRoZXIgdGhlc2Ugc3RhdGVtZW50cyBhcmUgVFJVRSBvciBGQUxTRSB0byBvdXIgYWR2YW50YWdlIGluIHRlc3RpbmcuIEZvciBleGFtcGxlLCB5b3UgY2FuIHRlc3Qgd2hldGhlciB0aGUgaW5wdXQgdG8gYSBmdW5jdGlvbiBpcyBhIHNwZWNpZmljIGRhdGEgdHlwZSwgbGVuZ3RoLCBvciBvdGhlciBjdXN0b20gYXJndW1lbnQgYnkgcGxhY2luZyB0aGUgYXNzZXJ0aW9uIGluc2lkZSB0aGUgZnVuY3Rpb24uIFlvdSBjYW4gYWxzbyBjdXN0b21pemUgdGhlIG91dHB1dCBtZXNzYWdlcy4KCgpMZXQncyB3cml0ZSBzb21lIGFzc2VydGlvbnMgaW50byBvdXIgZnVuY3Rpb24gdG8gdGVzdCB0aGF0OgoKLW91ciBpbnB1dCBpcyBhIGNoYXJhY3RlciB2ZWN0b3IKLW91ciBpbnB1dCBpcyBpbiBvdXIgbGlzdCBvZiBUYXhhIChubyB0eXBvcyEpCi1vdXIgaW5wdXQgaXMgb25seSAxIHZhbHVlCgpXZSB3aWxsIHRoZW4gc2VlIGhvdyB3ZWxsIHRoaXMgdGFrZXMgY2FyZSBvZiBhbGwgb2Ygb3VyIGlucHV0cyB0aGF0ICdicm9rZScgdGhlIGZ1bmN0aW9uIGVhcmxpZXIuIFdlIHdpbGwgYXNzZXNzIHRoZSBlcnJvciBtZXNzYWdlcyB0byBzZWUgaWYgdGhleSBhcmUgY2xlYXIuCgpgYGB7cn0KbWVhbl9mdW5jIDwtIGZ1bmN0aW9uKFRheGEpIHsKICAgICNhc3NlcnRpb25zIHRvIHRlc3QgaW5wdXQKICAgIGFzc2VydF90aGF0KGlzLmNoYXJhY3RlcihUYXhhKSkgCiAgICBhc3NlcnRfdGhhdChUYXhhICVpbiUgZGF0JFRheGEpIAogICAgYXNzZXJ0X3RoYXQobGVuZ3RoKFRheGEpID09IDEpCiAgICAKICAgIHN1bW1hcnkgPC0gZmlsdGVyKGRhdCwgVGF4YSA9PSBUYXhhKSAlPiUgCiAgICBzdW1tYXJpemUobWVhbiA9IG1lYW4oT1RVcyksIHNkID0gc2QoT1RVcykpCiAKICAgIHByaW50KHBhc3RlKCdUaGUgbWVhbiBmb3InLCBUYXhhLCdpcycsIHJvdW5kKHN1bW1hcnkkbWVhbiwyKSkpCiAgICByZXR1cm4oc3VtbWFyeSkKICAgIAoKfQoKYGBgCgpJIHVzdWFsbHkgdGVzdCB0byBtYWtlIHN1cmUgaXQgc3RpbGwgd29ya3Mgb24gc29tZXRoaW5nIEkgZXhwZWN0IGl0IHRvIHdoZW4gSSBtYWtlIGNoYW5nZXMgdG8gbXkgY29kZS4KCmBgYHtyIGVycm9yID0gVFJVRX0KbWVhbl9mdW5jKCJCYWNpbGxpIikKCm1lYW5fZnVuYyhjKCJDbG9zdHJpZGlhIiwgIkJhY3Rlcm9pZGlhIiwgIkJhY2lsbGkiKSkKCmBgYApNeSBmdW5jdGlvbiBpcyBzdGlsbCB3b3JraW5nIG9uIGV4cGVjdGVkIGlucHV0LgoKRm9yIGEgdmVjdG9yIG9mIGxlbmd0aCAzLCB3ZSByZWNlaXZlIHRoZSBlcnJvciB0aGF0IHRoZSAnbGVuZ3RoIG9mIHRoZSBhc3NlcnRpb24gaXMgbm90IDEnLiBUaGlzIGNvdWxkIGJlIG1vcmUgZXhwbGljaXQgZm9yIGEgdXNlciBpZS4gIlBsZWFzZSBpbnB1dCBvbmx5IG9uZSBUYXhhIiwgYnV0IGl0IGlzIG5vdCBiYWQuICBOb3RlIHRoYXQgdXNpbmcgYGFzc2VydF90aGF0KClgIHdlIGFyZSBub3QgZ2V0dGluZyBhbiBvdXRwdXQuIFRoZSBmdW5jdGlvbiBzdG9wcyB3aGVuIGl0IGdldHMgYSB2YWx1ZSB0aGF0IGlzIEZBTFNFIGFuZCBkb2VzIG5vdCBjb250aW51ZSBldmFsdWF0aW9uLiBUaGlzIGlzIG11Y2ggYmV0dGVyIHRoYW4gcHJldmlvdXNseSB3aGVuIHdlIHN0aWxsIGdvdCBhbiBvdXRwdXQgdGhhdCB3YXMgaW5jb3JyZWN0IGFuZCBkaWQgbm90IGdldCBhbnkgd2FybmluZy4gVGhpcyBhbHNvIG1lYW5zIHRoYXQgdGhpcyBwYXNzZWQgdGhlIGFzc2VydGlvbnMgZm9yIGJlaW5nIGEgY2hhcmFjdGVyIHZlY3RvciBhbmQgYmVpbmcgaW4gdGhlIFRheGEgaW4gb3VyIGRhdGFzZXQgd2l0aG91dCBlcnJvci4KCmBgYHtyIGVycm9yID0gVFJVRX0KbWVhbl9mdW5jKCJCYWNpbGxsbGkiKQoKYGBgCgpgYGB7ciBlcnJvciA9IFRSVUV9Cm1lYW5fZnVuYyhOQSkKbWVhbl9mdW5jKDIxMCkKbWVhbl9mdW5jKCkKYGBgCgpNb3N0IG9mIHRoZXNlIGVycm9yIG1lc3NhZ2VzIGFyZSBpbmZvcm1hdGl2ZSwgYnV0IG1heWJlIG5vdCBhcyBleHBsaWNpdCBhcyB0aGV5IGNvdWxkIGJlLiBMZXQncyBjdXN0b21pemUgb3VyIG1lc3NhZ2VzLgoKYGBge3J9Cm1lYW5fZnVuYyA8LSBmdW5jdGlvbihUYXhhKSB7CiAgICAjYXNzZXJ0aW9ucyB0byB0ZXN0IGlucHV0CiAgICBhc3NlcnRfdGhhdChpcy5jaGFyYWN0ZXIoVGF4YSksIG1zZyA9IHBhc3RlKCJUYXhhIGlzIGV4cGVjdGVkIHRvIGJlIG9mIHR5cGUgY2hhcmFjdGVyLiBZb3UgaGF2ZSBpbnB1dCB0eXBlIiwgdHlwZW9mKFRheGEpLCAiLiIpKSAKICAgIGFzc2VydF90aGF0KFRheGEgJWluJSBkYXQkVGF4YSwgbXNnID0gcGFzdGUoVGF4YSwgImlzIG5vdCBhIFRheGEgaW4gdGhlIGRhdGFzZXQuIikpCiAgICBhc3NlcnRfdGhhdChsZW5ndGgoVGF4YSkgPT0gMSwgbXNnID0gIlBsZWFzZSBpbnB1dCBvbmx5IG9uZSBUYXhhLiIpCiAgICBzdW1tYXJ5IDwtIGZpbHRlcihkYXQsIFRheGEgPT0gVGF4YSkgJT4lIAogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQogCiAgICBwcmludChwYXN0ZSgnVGhlIG1lYW4gZm9yJywgVGF4YSwnaXMnLCByb3VuZChzdW1tYXJ5JG1lYW4sMiksICIuIikpCiAgICByZXR1cm4oc3VtbWFyeSkKICAgIAoKfQpgYGAKCldlIGNvdWxkIGJlIGV2ZW4gbW9yZSBoZWxwZnVsIGFuZCBzaG93IHRoZSBvcHRpb24gb2YgYWxsIFRheGEgdG8gY2hvb3NlIGZyb20sIG9yIGNvbnZlcnQgZXZlcnl0aGluZyB0byBsb3dlcmNhc2UgdG8gcmVkdWNlIHRoZSBjaGFuY2Ugb2YgZXJyb3JzIGZyb20gaGF2aW5nIGEgbWl4IG9mIHVwcGVyY2FzZSBhbmQgbG93ZXJjYXNlLiBUaGVzZSBhcmUgdGhpbmdzIHlvdSBzaG91bGQgdGhpbmsgYWJvdXQsIGVzcGVjaWFsbHkgaWYgeW91IGFyZSBwYXNzaW5nIG9uIHlvdXIgY29kZSBvciBtYWtpbmcgYSBwYWNrYWdlIHlvdXJzZWxmLiBUaGUgZWFzaWVyIHBlb3BsZSBjYW4gdXNlIHlvdXIgZnVuY3Rpb25zLCB0aGUgbW9yZSBhZG9wdGFibGUgdGhleSB3aWxsIGJlLgoKVGhlcmUgYXJlIGEgZmV3IGZ1bmN0aW9ucyBpbmNsdWRlZCBpbiBgYXNzZXJ0dGhhdGAgZm9yIGNvbW1vbmx5IHVzZWQgYXNzZXJ0aW9ucy4gYGlzLnN0cmluZygpYCBsb29rcyBmb3IgYSBjaGFyYWN0ZXIgdmVjdG9yIG9mIGxlbmd0aCAxIChjb21iaW5pbmcgMiBvZiBvdXIgYWJvdmUgYXNzZXJ0aW9ucykuIFNpbWlsYXJpbHkgYGlzLmNvdW50KClgIGxvb2tzIGZvciBhIHNpbmdsZSBwb3NpdGl2ZSBpbnRlZ2VyLiBgbm9OQSgpYCB0ZXN0cyB0aGUgYXNzZXJ0aW9uIHRoYXQgdGhlcmUgYXJlIG5vIE5BcyAoZGlmZmVyZW50IGZyb20gYGlzLm5hKClgIGJlY2F1c2UgYXNzZXJ0aW9ucyBhcmUgVFJVRSkuIFJlbWVtYmVyIHRoYXQgdGhlIG9yZGVyIG9mIHlvdXIgYXNzZXJ0aW9ucyB3aWxsIGFmZmVjdCB0aGUgZXJyb3IgbWVzc2FnZSB5b3UgcmVjZWl2ZS4gRm9yIGV4YW1wbGUsIGFuIE5BIGlzIG5vdCBhIHN0cmluZywgYnV0IHRoaXMgZmFjdCBtYXkgYmUgbWFza2VkIGlmIHRoZSBjaGVjayBmb3IgYSBzdHJpbmcgcHJlY2VkZXMgdGhlIGNoZWNrIGZvciBhbiBOQSBzaW5jZSB0aGUgZnVuY3Rpb24gd2lsbCBzdG9wIGF0IHRoZSBmaXJzdCBGQUxTRSBldmFsdWF0aW9uLgoKYG5vdF9lbXB0eSgpYCB0ZXN0cyB0aGF0IGFsbCBkaW1lbnNpb25zIGFyZSBncmVhdGVyIHRoYW4gMC4KCkxldCdzIGNoYW5nZSBhIGZldyBhc3NlcnRpb25zIHRvIHRlc3QgZm9yIGFuIGVtcHR5IGZ1bmN0aW9uIGNhbGwsIE5BIGFzIGFuIGlucHV0LCBhbmQgdGhlIHVzZSBvZiBgaXMuc3RyaW5nKClgLgoKYGBge3J9Cm1lYW5fZnVuYyA8LSBmdW5jdGlvbihUYXhhKSB7CiAgICAKICAgIGFzc2VydF90aGF0KG5vdF9lbXB0eShUYXhhKSkKICAgIGFzc2VydF90aGF0KG5vTkEoVGF4YSkpIAogICAgYXNzZXJ0X3RoYXQoaXMuc3RyaW5nKFRheGEpKSAKICAgIGFzc2VydF90aGF0KFRheGEgJWluJSBkYXQkVGF4YSwgbXNnID0gcGFzdGUoVGF4YSwgImlzIG5vdCBhIFRheGEgaW4gdGhlIGRhdGFzZXQuIikpIAogICAgCiAgICBzdW1tYXJ5IDwtIGZpbHRlcihkYXQsIFRheGEgPT0gVGF4YSkgJT4lIAogICAgc3VtbWFyaXplKG1lYW4gPSBtZWFuKE9UVXMpLCBzZCA9IHNkKE9UVXMpKQogCiAgICBwcmludChwYXN0ZSgnVGhlIG1lYW4gZm9yJywgVGF4YSwnaXMnLCByb3VuZChzdW1tYXJ5JG1lYW4sMikpKQogICAgcmV0dXJuKHN1bW1hcnkpCiAgICAKCn0KYGBgCgoKCmBgYHtyIGVycm9yID0gVFJVRX0KbWVhbl9mdW5jKCJCYWNpbGxpIikKCm1lYW5fZnVuYyhjKCJDbG9zdHJpZGlhIiwgIkJhY3Rlcm9pZGlhIiwgIkJhY2lsbGkiKSkKbWVhbl9mdW5jKCJCYWNpbGxsbGkiKQptZWFuX2Z1bmMoTkEpCm1lYW5fZnVuYygyMTApCm1lYW5fZnVuYygpCmBgYApXZSBhcmUgdG9sZCB0aGVyZSBpcyBhIG1pc3NpbmcgdmFsdWUsIGJ1dCBub3Qgd2hlcmUgaXMgaXMuIChjYXNlIGZvciBkYXRhIGZyYW1lcz8pCgpUaGVyZSBhcmUgbW9yZSBvZiB0aGVzZSBoZWxwZXIgZnVuY3Rpb25zIHRvIGBhc3NlcnRfdGhhdCgpYCB3aGljaCBJIGVuY291cmFnZSB5b3UgdG8gY2hlY2sgb3V0IGluIHRoZSBoZWxwIG1lbnUuCgoKCiMjTXVsdGlwbGUgSW5wdXRzCgpTbyBmYXIsIHdlIGhhdmUgc2VlbiBmdW5jdGlvbnMgdGhhdCBjYW4gYmUgcmV1c2VkIGZvciBkaWZmZXJlbnQgaW5wdXRzLiBXaGF0IGlmIHdlIHdhbnQgdG8gcnVuIHRoZSBmdW5jdGlvbiBmb3IgbWFueSBpbnB1dHM/IFdoYXQgaWYgSSB3YW50IHRoZSBmdW5jdGlvbiB0byBiZSBhYmxlIHRvIHRha2UgMjAgVGF4YT8gV2UgbmVlZCB0byBiZSBhYmxlIHRvIGl0ZXJhdGUgb3ZlciBtdWx0aXBsZSBpbnB1dHMsIG9uZSAgYXQgYSB0aW1lLCBpbiBhIGNvbnRyb2xsZWQgbWFubmVyLiBFbnRlciBjb250cm9sIHN0YXRlbWVudHMuIFdpdGggY29udHJvbCBzdGF0ZW1lbnRzLCBhbiBleHByZXNzaW9uIGlzIHNldCB0aGF0IGlmIGV2YWx1YXRlZCB0byBiZSBUUlVFIHdpbGwgY29udGludWUgcHJvY2VlZCBhbmQgaWYgZXZhbHVhdGVkIHRvIGJlIEZBTFNFIHdpbGwgbm90LgoKRm9yIGxvb3BzIHdpbGwgaXRlcmF0ZSB0aHJvdWdoIGEgc3BlY2lmaWVkIG51bWJlciBvZiBpdGVtcy4KU3RhcnRpbmcgd2l0aCBhIHNpbXBsZSBsb29wIHRvIHByaW50IHRoZSBudW1iZXJzIDEgdGhyb3VnaCAxMC4gClRoaXMgZm9yIHN0YXRlbWVudCB3aWxsIGl0ZXJhdGUgdGhyb3VnaCB0aGUgdmVjdG9yIG9mIG51bWJlcnMgMToxMC4KSW4gdGhpcyBjYXNlIGkgd2lsbCBiZSAxLDIsMyw0LDUsNiw3LDgsOSwgdGhlbiAxMC4KCmBgYHtyfQpmb3IgKGkgaW4gMToxMCl7CiAgcHJpbnQoaSkKfQpgYGAKT2Z0ZW4sIHdlIHdhbnQgdG8gaXRlcmF0ZSBvdmVyIHRoZSBjb250ZW50cyBvZiBhIHZlY3RvciwgcmF0aGVyIHRoYW4gYSBudW1iZXIuIFRoaXMgaXMgcmVhbGx5IHRoZSBzYW1lIHRoaW5nLgoKCmBgYHtyfQp4IDwtIGMoInJhYmJpdCIsICJkb2ciLCAibGxhbWEiKSAKCmZvciAoaSBpbiB4KXsKICBwcmludChpKQp9CgpgYGAKWW91IGNhbiBzdWJzZXQgZm9yIHRoZSBhY3R1YWwgdmFsdWUuCgpgYGB7cn0KeCA8LSBjKCJyYWJiaXQiLCAiZG9nIiwgImxsYW1hIikgCgpmb3IgKGkgaW4gMTpsZW5ndGgoeCkpewogIHByaW50KHhbaV0pCn0KYGBgCk9yIHVzZSBudW1iZXJzLgoKYGBge3J9CnggPC0gYygicmFiYml0IiwgImRvZyIsICJsbGFtYSIpIAoKZm9yIChpIGluIDE6bGVuZ3RoKHgpKXsKICBwcmludChpKQp9CmBgYAoKV2h5IHVzZSBudW1iZXJzPwoKCmBgYHtyfQp4IDwtIGMoInJhYmJpdCIsICJkb2ciLCAibGxhbWEiKSAKCmZvciAoaSBpbiAxOmxlbmd0aCh4KSl7CiAgaWYgKGkgPCAzKXsKICBwcmludChpKQogIH0KfQpgYGAKCgoKCmBgYHtyfQpmb3IgKGkgaW4gZGF0JFRheGEpewogIHByaW50KGkpCiAgYnJlYWsKfQpgYGAKCgoKCmBgYHtyfQpmb3IgKGkgaW4gMTpucm93KGRhdCkpewogIHByaW50KGRhdFtpLDFdKQogIGJyZWFrCn0KYGBgCgoKCgpgYGB7cn0KbWVhbl9mdW5jIDwtIGZ1bmN0aW9uKFRheGEpIHsKICAgIGZvciAoaSBpbiBUYXhhKXsKICAgIHN1bW1hcnkgPC0gZmlsdGVyKGRhdCwgVGF4YSA9PSBpKSAlPiUgCiAgICBzdW1tYXJpemUobWVhbiA9IG1lYW4oT1RVcyksIHNkID0gc2QoT1RVcykpCiAgICAKICAgIHByaW50KHBhc3RlKCdUaGUgbWVhbiBmb3InLCBpLCdpcycsIHJvdW5kKHN1bW1hcnkkbWVhbiwyKSkpCiAgICB9CiAgICByZXR1cm4oc3VtbWFyeSkKCgp9CgptZWFuX2Z1bmMoYygiQ2xvc3RyaWRpYSIsICJCYWN0ZXJvaWRpYSIsICJCYWNpbGxpIikpCgpgYGAKCgoKCgoKCgoKYGBge3J9Cm1lYW5fZnVuYyA8LSBmdW5jdGlvbih4LCBmdW5zID0gbGlzdChtZWFuID0gbWVhbiwgc2QgPSBzZCkpIHsKICBkYXQgPC0gZmlsdGVyKGRhdCwgVGF4YSA9PSB4KSAKICBsYXBwbHkoZnVucywgZnVuY3Rpb24oZikgZihkYXQkT1RVcykpCiAgcmV0dXJuKGRhdCkKfQpgYGAKCgpgYGB7cn0KI3VzaW5nIHNhcHBseQoKbG9hZF9wYWNrYWdlcyA8LWZ1bmN0aW9uKHBhY2thZ2VfbmFtZXMpewogICAgbmV3IDwtIHBhY2thZ2VfbmFtZXNbIXBhY2thZ2VfbmFtZXMgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXV0KICAgIGlmICghcGFja2FnZSAlaW4lIGluc3RhbGxlZC5wYWNrYWdlcygpWywiUGFja2FnZSJdKXsKICAgICAgIGluc3RhbGwucGFja2FnZXMobmV3LHJlcG9zPSJodHRwOi8vY3Jhbi51dHN0YXQudXRvcm9udG8uY2EvIiwgZGVwZW5kZW5jaWVzID0gVFJVRSkKICAgIH0KICAgIHNhcHBseShwYWNrYWdlX25hbWVzLCByZXF1aXJlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUsIHZlcmJvc2UgPSBGQUxTRSkKfQoKCnBhY2thZ2VzIDwtIGMoImdncGxvdDIiLCAicGx5ciIsICJyZXNoYXBlMiIsICJSQ29sb3JCcmV3ZXIiLCAic2NhbGVzIiwgImdyaWQiKQpsb2FkX3BhY2thZ2VzKHBhY2thZ2VzKQoKI3VzaW5nIGEgZm9yIGxvb3AKCmxvYWRfcGFja2FnZXMgPC1mdW5jdGlvbihwYWNrYWdlX25hbWVzKXsKICBmb3IgKHBhY2thZ2UgaW4gcGFja2FnZV9uYW1lcyl7CiAgICBpZiAoIXBhY2thZ2UgJWluJSBpbnN0YWxsZWQucGFja2FnZXMoKVssIlBhY2thZ2UiXSl7CiAgICAgICBpbnN0YWxsLnBhY2thZ2VzKHBhY2thZ2UscmVwb3M9Imh0dHA6Ly9jcmFuLnV0c3RhdC51dG9yb250by5jYS8iLCBkZXBlbmRlbmNpZXMgPSBUUlVFKQogICAgfQogICAgbGlicmFyeShwYWNrYWdlLCBjaGFyYWN0ZXIub25seSA9IFRSVUUsIHZlcmJvc2U9RkFMU0UpCiAgfQp9Cgpsb2FkX3BhY2thZ2VzKGMoImZvcmVhY2giLCJNQVNTIiwiZG9QYXJhbGxlbCIpKQoKCmBgYAoKCgoKCgoKCgoKZ2VuZXJhbGl6ZSB0byBjb3VudHJ5LCBiYWN0ZXJpYSwgI290dXMgYWJvdmUvYmVsb3cgbWVhbj8gcmFua2luZyBwb3NpdGlvbj8KCgoKX0Jhc2ljIFN5bnRheCBvZiBGdW5jdGlvbnMgaW4gUl8KCi0gdGhlIHN0cnVjdHVyZSBvZiBhIGZ1bmN0aW9uIChmb3JtYWxzLCBib2R5LCBwYXJlbnQgZW52aXJvbm1lbnQpCi0gdW5kZXJzdGFuZGluZyBzY29waW5nCi0gbmFtaW5nIGNvbnZlbnRpb25zCi0gZ2VuZXJhbGl6aW5nIGEgZnVuY3Rpb24KLSBzZXR0aW5nIGRlZmF1bHQgdmFsdWVzCi0gYWRkaW5nICcuLi4nCi0gdGhlIG91dHB1dCBvZiB5b3VyIGZ1bmN0aW9uCi0gc291cmNpbmcgZnVuY3Rpb25zCi0gTkFzIGluIGZ1bmN0aW9ucwotIHJldHVybgogIApfQ29udHJvbCBTdHJ1Y3R1cmVzXwotIGlmIGVsc2UKLSBmb3IKLSB3aGlsZQotIHJlcGVhdAotIGJyZWFrCi0gbmV4dAogIAogIApfSGFuZGxpbmcgRXJyb3JzXwoKLSBzdG9waWZub3QoKSwgd2FybmluZygpLCBzdXBwcmVzc1dhcm5pbmdzKCksIHRyeUNhdGNoKCkKLSBpZiBzdG9wCgpfVGVzdGluZyBBcmd1bWVudHMgYW5kIFZhbGlkaXR5IG9mIHlvdXIgRnVuY3Rpb25fCgotIGluZm9ybWFsIHRlc3RpbmcKLSB0ZXN0dGhhdCgpIGZvciBmb3JtYWwgdW5pdCB0ZXN0aW5nCi0gYXNzZXJ0dGhhdAoKX1Vwc2NhbGluZyB3aXRoIGZ1bmN0aW9uc18KCmZ1bmN0aW9ucyBjYW4gYmU6CgotIGFzc2lnbmVkIGluIHZhcmlhYmxlcwotIHN0b3JlZCBpbiBsaXN0cwotIHBhc3NlZCBhcyBhcmd1bWVudHMgdG8gb3RoZXIgZnVuY3Rpb25zCi0gY3JlYXRlZCBpbnNpZGUgb3RoZXIgZnVuY3Rpb25zCi0gb3V0cHV0IGJ5IG90aGVyIGZ1bmN0aW9ucwoKRFJZIHByaW5jaXBsZSAtIERvbid0IFJlcGVhdCBZb3Vyc2VsZgoKCgoKKioqCl9fQ2hhbGxlbmdlX18gCgoKPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDttYXJnaW46MCAxMHB4IDEwcHggMCIgbWFya2Rvd249IjEiPgohW10oaW1nL1BlRWVLN0guZ2lmKXt3aWR0aD0xNTBweH0KCjwvZGl2PgoKV3JpdGUgYSBmdW5jdGlvbiB0byBjaGVjayB0aGF0IHRoZSBwYWNrYWdlcyBjKCJkcGx5ciIsICJyZWFkeG1sIiwgInRpZHlyIikgYXJlIGluc3RhbGxlZCBhbmQgdG8gbG9hZCB0aGUgcGFja2FnZXMuIFRoZSBmdW5jdGlvbiBzaG91bGQgaW5zdGFsbCB0aGUgcGFja2FnZXMgaWYgdGhleSBhcmUgbm90IGFscmVhZHkgaW5zdGFsbGxlZC4gV3JpdGUgYSB3YXJuaW5nIGlmIHRoZSBwYWNrYWdlIGlzIG5vdCBpbnN0YWxsZWQsIGJ1dCBpcyBiZWluZyBpbnN0YWxsZWQuIFdpbGwgdGhpcyBmdW5jdGlvbiB3b3JrIGZvciBvdGhlciBwYWNrYWdlcz8KCgo8L2JyPgo8L2JyPgo8L2JyPgoKKioqCgoKKioqCl9fQ2hhbGxlbmdlX18gCgoKPGRpdiBzdHlsZT0iZmxvYXQ6bGVmdDttYXJnaW46MCAxMHB4IDEwcHggMCIgbWFya2Rvd249IjEiPgohW10oaW1nL2d1aW5lYS1waWctc3R1ZmZpbmctaGlzLWZhY2Utd2l0aC1jYXJyb3QtMjUzMS5qcGcpe3dpZHRoPTE1MHB4fQoKPC9kaXY+CgpXcml0ZSBhIGZ1bmN0aW9uIHRvIHJlYWQgYWxsIC5jc3YgZmlsZXMgaW4gYSBkaXJlY3RvcnkgaW50byBSIGFuZCBzYXZlIGVhY2ggb2YgdGhlbSBpbiBhIHNlcGFyYXRlIGRhdGEgZnJhbWUuCgoKYGBge3J9CgojTm90ZTogbmVlZCB0byB0ZWFjaCBob3cgdG8gZG8gd2l0aCBhIGZvciBsb29wIGFzIHdlbGwgYXMgYXBwbHkKCgoKcGF0aCA9ICJ+L0Rlc2t0b3AvUi8iCgpyZWFkX2ZpbGVzIDwtIGZ1bmN0aW9uKHBhdGgsIGxvZ2ljKXsKCmZpbGVuYW1lcyA8LSBsaXN0LmZpbGVzKHBhdGg9IHBhdGgsCiAgICAgICAgICAgICAgICAgICAgICAgIHBhdHRlcm49Ii4qY3N2IikKCmZpbGVsaXN0IDwtIGxhcHBseShwYXN0ZTAocGF0aCwgZmlsZW5hbWVzKSwgcmVhZC5jc3YsIGhlYWRlciA9IGxvZ2ljKQoKI2lmIG5lY2Vzc2FyeSwgYXNzaWduIG5hbWVzIHRvIGRhdGEuZnJhbWVzCm5hbWVzKGZpbGVsaXN0KSA8LSBnc3ViKCJcXC5jc3YiLCAiIiwgZmlsZW5hbWVzKQoKbGFwcGx5KG5hbWVzKGZpbGVsaXN0KSwgZnVuY3Rpb24oeCkgYXNzaWduKHgsZmlsZWxpc3RbW3hdXSxlbnZpcj0uR2xvYmFsRW52KSkKCn0KCgpyZWFkX2ZpbGVzKHBhdGgsIEZBTFNFKQoKZmlsZWxpc3QgPC0gbGlzdC5maWxlcyhwYXRoPXBhdGgsIHBhdHRlcm49IiouY3N2IikgIyBjcmVhdGUgbGlzdCBvZiBhbGwgLmNzdiBmaWxlcyBpbiBmb2xkZXIKCiNpZiBuZWNlc3NhcnksIGFzc2lnbiBuYW1lcyB0byBkYXRhLmZyYW1lcwpuYW1lcyhmaWxlbGlzdCkgPC0gZ3N1YigiXFwuY3N2IiwgIiIsIGZpbGVsaXN0KQoKIyByZWFkIGluIGVhY2ggLmNzdiBmaWxlIGluIGZpbGVfbGlzdCBhbmQgY3JlYXRlIGEgZGF0YSBmcmFtZSB3aXRoIHRoZSBzYW1lIG5hbWUgYXMgdGhlIC5jc3YgZmlsZQpmb3IgKGkgaW4gMTpsZW5ndGgoZmlsZWxpc3QpKXsKICBhc3NpZ24obmFtZXMoZmlsZWxpc3QpW2ldLCAKICByZWFkLmNzdihwYXN0ZTAocGF0aCwgZmlsZWxpc3RbaV0pLCBoZWFkZXIgPSBGKQogICl9CgoKIyByZWFkIGluIGVhY2ggLmNzdiBmaWxlIGluIGZpbGVfbGlzdCBhbmQgcmJpbmQgdGhlbSBpbnRvIGEgZGF0YSBmcmFtZSBjYWxsZWQgZGF0YSAKZGF0YSA8LSAKICBkby5jYWxsKCJyYmluZCIsIAogICAgICAgICAgbGFwcGx5KGZpbGVfbGlzdCwgCiAgICAgICAgICAgICAgICAgZnVuY3Rpb24oeCkgCiAgICAgICAgICAgICAgICAgcmVhZC5jc3YocGFzdGUwKHBhdGgsIHgpLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpKSkKYGBgCgoKPC9icj4KPC9icj4KPC9icj4KCioqKgoKCgpfX1Jlc291cmNlczpfXyAKCjxodHRwOi8vc3RhdDU0NS5jb20vYmxvY2swMTFfd3JpdGUteW91ci1vd24tZnVuY3Rpb24tMDEuaHRtbD4gICAgIAo8aHR0cDovL3N0YXQ1NDUuY29tL2Jsb2NrMDExX3dyaXRlLXlvdXItb3duLWZ1bmN0aW9uLTAyLmh0bWw+ICAgICAKPGh0dHA6Ly9zdGF0NTQ1LmNvbS9ibG9jazAxMV93cml0ZS15b3VyLW93bi1mdW5jdGlvbi0wMy5odG1sPiAgICAgCjxodHRwOi8vbWF6YW1hc2NpZW5jZS5jb20vV29ya2luZ1dpdGhEYXRhLz9wPTkxMj4KPGh0dHA6Ly9hZHYtci5oYWQuY28ubnovRnVuY3Rpb25zLmh0bWw+CgoKI1Bvc3QtTGVzc29uIEFzc2Vzc21lbnQKKioqCgpZb3VyIGZlZWRiYWNrIGlzIGVzc2VudGlhbCB0byBoZWxwIHRoZSBuZXh0IGNvaG9ydCBvZiB0cmFpbmVlcy4gUGxlYXNlIHRha2UgYSBtaW51dGUgdG8gY29tcGxldGUgdGhlIGZvbGxvd2luZyBzaG9ydCBzdXJ2ZXk6Cmh0dHBzOi8vd3d3LnN1cnZleW1vbmtleS5jb20vci9YNUMySkdSCgo8L2JyPgoKKioqCgo8L2JyPgoKVGhhbmtzIGZvciBjb21pbmchISEKCiFbXShpbWcvcnN0dWRpby1ib21iLnBuZyl7d2lkdGg9MzAwcHh9